#pragma once

#include "dllmain.h"

BEGIN_NAMESPACE_CPPX

/*  the type for screen and DC coordinates */
typedef int fuCoord;

enum {  fuDefaultCoord = -1 };

enum fuOrientation
{
    /* don't change the values of these elements, they are used elsewhere */
    fuHORIZONTAL              = 0x0004,
    fuVERTICAL                = 0x0008,

    fuBOTH                    = fuVERTICAL | fuHORIZONTAL,

    /*  a mask to extract orientation from the combination of flags */
    fuORIENTATION_MASK        = fuBOTH
};

// ---------------------------------------------------------------------------
// fuSize
// ---------------------------------------------------------------------------

class _CPPX_API fuSize
{
public:
    // members are public for compatibility, don't use them directly.
    int x, y;

    // constructors
    fuSize() : x(0), y(0) { }
    fuSize(int xx, int yy) : x(xx), y(yy) { }

    // no copy ctor or assignment operator - the defaults are ok

    fuSize& operator+=(const fuSize& sz) { x += sz.x; y += sz.y; return *this; }
    fuSize& operator-=(const fuSize& sz) { x -= sz.x; y -= sz.y; return *this; }
    fuSize& operator/=(int i) { x /= i; y /= i; return *this; }
    fuSize& operator*=(int i) { x *= i; y *= i; return *this; }
    fuSize& operator/=(unsigned int i) { x /= i; y /= i; return *this; }
    fuSize& operator*=(unsigned int i) { x *= i; y *= i; return *this; }
    fuSize& operator/=(long i) { x /= i; y /= i; return *this; }
    fuSize& operator*=(long i) { x *= i; y *= i; return *this; }
    fuSize& operator/=(unsigned long i) { x /= i; y /= i; return *this; }
    fuSize& operator*=(unsigned long i) { x *= i; y *= i; return *this; }
    fuSize& operator/=(double i) { x = int(x/i); y = int(y/i); return *this; }
    fuSize& operator*=(double i) { x = int(x*i); y = int(y*i); return *this; }

    void IncTo(const fuSize& sz)
        { if ( sz.x > x ) x = sz.x; if ( sz.y > y ) y = sz.y; }
    void DecTo(const fuSize& sz)
        { if ( sz.x < x ) x = sz.x; if ( sz.y < y ) y = sz.y; }

    void IncBy(int dx, int dy) { x += dx; y += dy; }
    void IncBy(const fuSize& sz) { IncBy(sz.x, sz.y); }
    void IncBy(int d) { IncBy(d, d); }

    void DecBy(int dx, int dy) { IncBy(-dx, -dy); }
    void DecBy(const fuSize& sz) { DecBy(sz.x, sz.y); }
    void DecBy(int d) { DecBy(d, d); }


    fuSize& Scale(float xscale, float yscale)
        { x = (int)(x*xscale); y = (int)(y*yscale); return *this; }

    // accessors
    void Set(int xx, int yy) { x = xx; y = yy; }
    void SetWidth(int w) { x = w; }
    void SetHeight(int h) { y = h; }

    int GetWidth() const { return x; }
    int GetHeight() const { return y; }

    bool IsFullySpecified() const { return x != fuDefaultCoord && y != fuDefaultCoord; }

    // combine this size with the other one replacing the default (i.e. equal
    // to fuDefaultCoord) components of this object with those of the other
    void SetDefaults(const fuSize& size)
    {
        if ( x == fuDefaultCoord )
            x = size.x;
        if ( y == fuDefaultCoord )
            y = size.y;
    }

    // compatibility
    int GetX() const { return x; }
    int GetY() const { return y; }
};

inline bool operator==(const fuSize& s1, const fuSize& s2)
{
    return s1.x == s2.x && s1.y == s2.y;
}

inline bool operator!=(const fuSize& s1, const fuSize& s2)
{
    return s1.x != s2.x || s1.y != s2.y;
}

inline fuSize operator+(const fuSize& s1, const fuSize& s2)
{
    return fuSize(s1.x + s2.x, s1.y + s2.y);
}

inline fuSize operator-(const fuSize& s1, const fuSize& s2)
{
    return fuSize(s1.x - s2.x, s1.y - s2.y);
}

inline fuSize operator/(const fuSize& s, int i)
{
    return fuSize(s.x / i, s.y / i);
}

inline fuSize operator*(const fuSize& s, int i)
{
    return fuSize(s.x * i, s.y * i);
}

inline fuSize operator*(int i, const fuSize& s)
{
    return fuSize(s.x * i, s.y * i);
}

inline fuSize operator/(const fuSize& s, unsigned int i)
{
    return fuSize(s.x / i, s.y / i);
}

inline fuSize operator*(const fuSize& s, unsigned int i)
{
    return fuSize(s.x * i, s.y * i);
}

inline fuSize operator*(unsigned int i, const fuSize& s)
{
    return fuSize(s.x * i, s.y * i);
}

inline fuSize operator/(const fuSize& s, long i)
{
    return fuSize(s.x / i, s.y / i);
}

inline fuSize operator*(const fuSize& s, long i)
{
    return fuSize(s.x * i, s.y * i);
}

inline fuSize operator*(long i, const fuSize& s)
{
    return fuSize(s.x * i, s.y * i);
}

inline fuSize operator/(const fuSize& s, unsigned long i)
{
    return fuSize(s.x / i, s.y / i);
}

inline fuSize operator*(const fuSize& s, unsigned long i)
{
    return fuSize(s.x * i, s.y * i);
}

inline fuSize operator*(unsigned long i, const fuSize& s)
{
    return fuSize(s.x * i, s.y * i);
}

inline fuSize operator*(const fuSize& s, double i)
{
    return fuSize(int(s.x * i), int(s.y * i));
}

inline fuSize operator*(double i, const fuSize& s)
{
    return fuSize(int(s.x * i), int(s.y * i));
}


// ----------------------------------------------------------------------------
// fuPoint: 2D point with integer coordinates
// ----------------------------------------------------------------------------

class _CPPX_API fuPoint
{
public:
    int x, y;

    fuPoint() : x(0), y(0) { }
    fuPoint(int xx, int yy) : x(xx), y(yy) { }

    // no copy ctor or assignment operator - the defaults are ok

    //assignment operators
    fuPoint& operator+=(const fuPoint& p) { x += p.x; y += p.y; return *this; }
    fuPoint& operator-=(const fuPoint& p) { x -= p.x; y -= p.y; return *this; }

    fuPoint& operator+=(const fuSize& s) { x += s.GetWidth(); y += s.GetHeight(); return *this; }
    fuPoint& operator-=(const fuSize& s) { x -= s.GetWidth(); y -= s.GetHeight(); return *this; }

    uint Distance(fuPoint & pt);
};


// comparison
inline bool operator==(const fuPoint& p1, const fuPoint& p2)
{
    return p1.x == p2.x && p1.y == p2.y;
}

inline bool operator!=(const fuPoint& p1, const fuPoint& p2)
{
    return !(p1 == p2);
}


// arithmetic operations (component wise)
inline fuPoint operator+(const fuPoint& p1, const fuPoint& p2)
{
    return fuPoint(p1.x + p2.x, p1.y + p2.y);
}

inline fuPoint operator-(const fuPoint& p1, const fuPoint& p2)
{
    return fuPoint(p1.x - p2.x, p1.y - p2.y);
}

inline fuPoint operator+(const fuPoint& p, const fuSize& s)
{
    return fuPoint(p.x + s.x, p.y + s.y);
}

inline fuPoint operator-(const fuPoint& p, const fuSize& s)
{
    return fuPoint(p.x - s.x, p.y - s.y);
}

inline fuPoint operator+(const fuSize& s, const fuPoint& p)
{
    return fuPoint(p.x + s.x, p.y + s.y);
}

inline fuPoint operator-(const fuSize& s, const fuPoint& p)
{
    return fuPoint(s.x - p.x, s.y - p.y);
}

inline fuPoint operator-(const fuPoint& p)
{
    return fuPoint(-p.x, -p.y);
}

// ---------------------------------------------------------------------------
// fuRect
// ---------------------------------------------------------------------------

class _CPPX_API fuRect
{
public:
    fuRect()
        : x(0), y(0), width(0), height(0)
        { }
    fuRect(int xx, int yy, int ww, int hh)
        : x(xx), y(yy), width(ww), height(hh)
        { }
    fuRect(const fuPoint& topLeft, const fuPoint& bottomRight);
    fuRect(const fuPoint& pt, const fuSize& size)
        : x(pt.x), y(pt.y), width(size.x), height(size.y)
        { }
    fuRect(const fuSize& size)
        : x(0), y(0), width(size.x), height(size.y)
        { }

    // default copy ctor and assignment operators ok

    int GetX() const { return x; }
    void SetX(int xx) { x = xx; }

    int GetY() const { return y; }
    void SetY(int yy) { y = yy; }

    int GetWidth() const { return width; }
    void SetWidth(int w) { width = w; }

    int GetHeight() const { return height; }
    void SetHeight(int h) { height = h; }

    fuPoint GetPosition() const { return fuPoint(x, y); }
    void SetPosition( const fuPoint &p ) { x = p.x; y = p.y; }

    fuSize GetSize() const { return fuSize(width, height); }
    void SetSize( const fuSize &s ) { width = s.GetWidth(); height = s.GetHeight(); }

    bool IsEmpty() const { return (width <= 0) || (height <= 0); }

    int GetLeft()   const { return x; }
    int GetTop()    const { return y; }
    int GetBottom() const { return y + height - 1; }
    int GetRight()  const { return x + width - 1; }

    void SetLeft(int left) { x = left; }
    void SetRight(int right) { width = right - x + 1; }
    void SetTop(int top) { y = top; }
    void SetBottom(int bottom) { height = bottom - y + 1; }

    fuPoint GetTopLeft() const { return GetPosition(); }
    fuPoint GetLeftTop() const { return GetTopLeft(); }
    void SetTopLeft(const fuPoint &p) { SetPosition(p); }
    void SetLeftTop(const fuPoint &p) { SetTopLeft(p); }

    fuPoint GetBottomRight() const { return fuPoint(GetRight(), GetBottom()); }
    fuPoint GetRightBottom() const { return GetBottomRight(); }
    void SetBottomRight(const fuPoint &p) { SetRight(p.x); SetBottom(p.y); }
    void SetRightBottom(const fuPoint &p) { SetBottomRight(p); }

    fuPoint GetTopRight() const { return fuPoint(GetRight(), GetTop()); }
    fuPoint GetRightTop() const { return GetTopRight(); }
    void SetTopRight(const fuPoint &p) { SetRight(p.x); SetTop(p.y); }
    void SetRightTop(const fuPoint &p) { SetTopLeft(p); }

    fuPoint GetBottomLeft() const { return fuPoint(GetLeft(), GetBottom()); }
    fuPoint GetLeftBottom() const { return GetBottomLeft(); }
    void SetBottomLeft(const fuPoint &p) { SetLeft(p.x); SetBottom(p.y); }
    void SetLeftBottom(const fuPoint &p) { SetBottomLeft(p); }

    // operations with rect
    fuRect& Inflate(fuCoord dx, fuCoord dy);
    fuRect& Inflate(const fuSize& d) { return Inflate(d.x, d.y); }
    fuRect& Inflate(fuCoord d) { return Inflate(d, d); }
    fuRect Inflate(fuCoord dx, fuCoord dy) const
    {
        fuRect r = *this;
        r.Inflate(dx, dy);
        return r;
    }

    fuRect& Deflate(fuCoord dx, fuCoord dy) { return Inflate(-dx, -dy); }
    fuRect& Deflate(const fuSize& d) { return Inflate(-d.x, -d.y); }
    fuRect& Deflate(fuCoord d) { return Inflate(-d); }
    fuRect Deflate(fuCoord dx, fuCoord dy) const
    {
        fuRect r = *this;
        r.Deflate(dx, dy);
        return r;
    }

    void Offset(fuCoord dx, fuCoord dy) { x += dx; y += dy; }
    void Offset(const fuPoint& pt) { Offset(pt.x, pt.y); }

    fuRect& Intersect(const fuRect& rect);
    fuRect Intersect(const fuRect& rect) const
    {
        fuRect r = *this;
        r.Intersect(rect);
        return r;
    }

    fuRect& Union(const fuRect& rect);
    fuRect Union(const fuRect& rect) const
    {
        fuRect r = *this;
        r.Union(rect);
        return r;
    }

    // return true if the point is (not strcitly) inside the rect
    bool Contains(int x, int y) const;
    bool Contains(const fuPoint& pt) const { return Contains(pt.x, pt.y); }
    // return true if the rectangle 'rect' is (not strictly) inside this rect
    bool Contains(const fuRect& rect) const;

    // return true if the rectangles have a non empty intersection
    bool Intersects(const fuRect& rect) const;

    // like Union() but don't ignore empty rectangles
    fuRect& operator+=(const fuRect& rect);

    // intersections of two rectrangles not testing for empty rectangles
    fuRect& operator*=(const fuRect& rect);

    // centre this rectangle in the given (usually, but not necessarily,
    // larger) one
    fuRect CentreIn(const fuRect& r, int dir = fuBOTH) const
    {
        return fuRect(dir & fuHORIZONTAL ? r.x + (r.width - width)/2 : x,
                      dir & fuVERTICAL ? r.y + (r.height - height)/2 : y,
                      width, height);
    }

    fuRect CenterIn(const fuRect& r, int dir = fuBOTH) const
    {
        return CentreIn(r, dir);
    }

public:
    int x, y, width, height;
};


// compare rectangles
inline bool operator==(const fuRect& r1, const fuRect& r2)
{
    return (r1.x == r2.x) && (r1.y == r2.y) &&
           (r1.width == r2.width) && (r1.height == r2.height);
}

inline bool operator!=(const fuRect& r1, const fuRect& r2)
{
    return !(r1 == r2);
}

// like Union() but don't treat empty rectangles specially
_CPPX_API
fuRect operator+(const fuRect& r1, const fuRect& r2);

// intersections of two rectangles
_CPPX_API
fuRect operator*(const fuRect& r1, const fuRect& r2);

END_NAMESPACE_CPPX
